﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using DotNetNuke.Services.Exceptions;

namespace HeroldIT.Dnn.Modules
{
    /// <summary>
    /// Extension method for working with various data.
    /// </summary>
    public static class HelperExtensions
    {
        /// <summary>
        /// Holds the Regular expression for recognizing strings containing html.
        /// </summary>
        private static readonly Regex HtmlRegex = new Regex(@"</?\w+[^>]*>", RegexOptions.Compiled | RegexOptions.CultureInvariant);

        private static readonly Regex HtmlContentElementsRegex = new Regex(@"<(?:button|embed|iframe|img|input|object|select|textarea)\b[^>]*>", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);

        /// <summary>
        /// Encodes a list of <see cref="Guid"/>s for use in settings.
        /// </summary>
        /// <param name="guids">The list of guids to encode.</param>
        /// <returns>A string containing the encoded guids.</returns>
        public static string ToSettingString(this ICollection<Guid> guids)
        {
            if (null == guids || guids.Count <= 0)
                return null;

            return String.Join(",", guids.Distinct().Select(g => g.ToString()).ToArray());
        }

        /// <summary>
        /// Encodes a list of <see cref="Guid"/>s for use in urls.
        /// </summary>
        /// <param name="guids">The list of guids to encode.</param>
        /// <returns>A string containing the encoded guids.</returns>
        public static string ToSguidString(this ICollection<Guid> guids)
        {
            if (null == guids || guids.Count <= 0)
                return null;

            return String.Join(",", guids.Distinct().Select(g => new Sguid(g).Value).ToArray());
        }

        /// <summary>
        /// Decodes a string encoded using <see cref="ToSettingString"/> or <see cref="ToSguidString"/>.
        /// </summary>
        /// <param name="guids">The string containing the previously encoded data.</param>
        /// <returns>A list of guids.</returns>
        public static IList<Guid> ToValueList(this string guids)
        {
            if (String.IsNullOrEmpty(guids))
                return null;

            var values = guids.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
            var ids = new HashSet<Guid>();
            foreach (var s in values)
            {
                try
                {
                    ids.Add(new Sguid(s));
                }
                catch (FormatException fe)
                {
                    Exceptions.LogException(fe);
                }
            }
            if (ids.Count <= 0)
                return null;

            return ids.ToList();
        }

        /// <summary>
        /// Gets whether a collection is null or empty.
        /// </summary>
        /// <param name="collection">The collection to check.</param>
        /// <returns><c>true</c> if the <paramref name="collection"/> is empty, otherwise <c>false</c>.</returns>
        public static bool IsNullOrEmpty(this ICollection collection)
        {
            return null == collection || collection.Count <= 0;
        }

        /// <summary>
        /// Gets whether a collection is null or empty.
        /// </summary>
        /// <typeparam name="T">The type of the <paramref name="collection"/>.</typeparam>
        /// <param name="collection">The collection to check.</param>
        /// <returns><c>true</c> if the <paramref name="collection"/> is empty, otherwise <c>false</c>.</returns>
        public static bool IsNullOrEmpty<T>(this ICollection<T> collection)
        {
            return null == collection || collection.Count <= 0;
        }

        /// <summary>
        /// Returns null, if the given string is empty.
        /// </summary>
        /// <param name="s">The string to check.</param>
        /// <returns><c>null</c> if the given string is empty, otherwise the string itself.</returns>
        public static string NullIfEmpty(this string s)
        {
            return String.IsNullOrEmpty(s) ? null : s;
        }

        /// <summary>
        /// Returns <c>null</c> or <see cref="String.Empty"/> if the given string <paramref name="s"/>
        /// is empty, depending on the value of <paramref name="nullOrEmpty"/>. If <paramref name="nullOrEmpty"/> is <see cref="String.Empty"/>, the result will be it also, otherwise it will return <c>null</c>.
        /// </summary>
        /// <param name="s">The string to check.</param>
        /// <param name="nullOrEmpty">The empty value to use. <see cref="String.Empty"/> to return it, all other values yield <c>null</c>.</param>
        /// <returns><c>null</c> or <see cref="String.Empty"/> if the given string is empty, otherwise the string itself.</returns>
        public static string NullIfEmpty(this string s, string nullOrEmpty)
        {
            return String.IsNullOrEmpty(s)
                       ? String.Equals(String.Empty, nullOrEmpty)
                             ? String.Empty
                             : null
                       : s;
        }

        /// <summary>
        /// Checks if a <see cref="string"/> has html content.
        /// </summary>
        /// <param name="s">The <see cref="string"/> to check for html content.</param>
        /// <returns><c>true</c> if the string contains html code, otherwise <c>false</c>.</returns>
        public static bool HasHtmlContent(this string s)
        {
            return s != null && HtmlRegex.IsMatch(s);
        }

        /// <summary>
        /// Checks if an html <see cref="string"/> is null or empty.
        /// </summary>
        /// <remarks>
        /// The check is performed by first checking, if the string contains any content html elements.
        /// If not, all html elements are removed, the string is HtmlDecoded, trimmed, and then checked
        /// for content.
        /// </remarks>
        /// <param name="s">The <see cref="string"/> to check for content.</param>
        /// <returns><c>true</c> if the string contains content, otherwise <c>false</c>.</returns>
        public static bool IsHtmlNullOrEmpty(this string s)
        {
            if (String.IsNullOrEmpty(s))
                return true;

            if (HtmlContentElementsRegex.IsMatch(s))
                return false;

            return String.IsNullOrEmpty(HttpUtility.HtmlDecode(HtmlRegex.Replace(s, "")).Trim());
        }
    }
}
